﻿using System;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace DataAccess
{
    public class Repository<TObject> : IRepository<TObject>
     where TObject : class
    {
        public Repository(IUnitOfWork unitOfWork)
        {
            Context = unitOfWork.DBContext;
        }

        protected DB Context = null;

        protected DbSet<TObject> DbSet
        {
            get
            {
                return Context.Set<TObject>();
            }
        }


        public virtual IQueryable<TObject> All()
        {
            return DbSet.AsNoTracking();
        }

        public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.Where(predicate).AsQueryable<TObject>();
        }

        public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> predicate, string include)
        {
            return DbSet.Where(predicate).Include(include).AsQueryable<TObject>().AsNoTracking<TObject>();
        }

        public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter, out int total, int index = 0, int size = 50)
        {
            int skipCount = index * size;
            var _resetSet = filter != null ? DbSet.Where(filter).AsQueryable() : DbSet.AsQueryable();
            _resetSet = skipCount == 0 ? _resetSet.Take(size) : _resetSet.Skip(skipCount).Take(size);
            total = _resetSet.Count();
            return _resetSet.AsNoTracking().AsQueryable();
        }

        public bool Contains(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.Any(predicate);
        }

        public virtual TObject Find(params object[] keys)
        {
            return DbSet.Find(keys);
        }

        public virtual TObject Find(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.FirstOrDefault(predicate);
        }

        public virtual TObject Find(Expression<Func<TObject, bool>> predicate, string include)
        {
            return DbSet.Include(include).FirstOrDefault(predicate);
        }

        public virtual TObject Create(TObject item)
        {
            var newEntry = DbSet.Add(item);
            Context.SaveChanges();
            return newEntry;
        }

        public virtual int Count
        {
            get
            {
                return DbSet.Count();
            }
        }

        public virtual int Delete(TObject item)
        {
            var entry = Context.Entry(item);
            if (entry.State == EntityState.Detached)
                DbSet.Attach(item);

            DbSet.Remove(item);
            return Context.SaveChanges();
        }

        public virtual int Update(TObject item)
        {
            var entry = Context.Entry(item);
            DbSet.Attach(item);
            entry.State = EntityState.Modified;
            return Context.SaveChanges();
        }

        public virtual void Edit(TObject item)
        {
            var entry = Context.Entry(item);
            DbSet.Attach(item);
            entry.State = EntityState.Modified;
        }

        public virtual void Add(TObject item)
        {
            DbSet.Add(item);
        }

        public virtual void Remove(Expression<Func<TObject, bool>> predicate)
        {
            var objects = Filter(predicate);
            foreach (var obj in objects)
                DbSet.Remove(obj);
        }

        public virtual int Delete(Expression<Func<TObject, bool>> predicate)
        {
            var objects = Filter(predicate);
            foreach (var obj in objects)
                DbSet.Remove(obj);
            return Context.SaveChanges();
        }
    }
}